package com.platform.modules.mall.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendRequest;
import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.platform.common.exception.BusinessException;
import com.platform.common.utils.*;
import com.platform.modules.mall.dao.MallCardSnDao;
import com.platform.modules.mall.entity.*;
import com.platform.modules.mall.service.*;
import com.platform.modules.order.OrderUtil;
import com.platform.modules.order.ParamsEntity;
import com.platform.modules.phone.entity.dto.PhoneTopUpDto;
import com.platform.modules.phone.service.PhoneTopUpService;
import com.platform.modules.sys.service.SysConfigService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 卡卷编码Service实现类
 *
 * @author tech
 * @date 2020-08-04 22:52:36
 */
@Service("mallCardSnService")
@Slf4j
public class MallCardSnServiceImpl extends ServiceImpl<MallCardSnDao, MallCardSnEntity> implements MallCardSnService {

    @Autowired
    MallUserCardService userCardService;
    @Autowired
    MallGoodsService goodsService;
    @Autowired
    MallOrderService orderService;
    @Autowired
    MallOrderGoodsService orderGoodsService;
    @Autowired
    MallUserService userService;
    @Autowired
    SendCoupon sendCoupon;

    @Autowired
    SendCouponWxService sendCouponWxService;
    @Autowired
    MallUserIntegralLogService mallUserIntegralLogService;
    @Autowired
    private PhoneTopUpService phoneTopUpService;
    @Resource
    private JedisUtil jedisUtil;

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private MallOrderGoodsService mallOrderGoodsService;


    // 微信立减金类型
    Integer weixinCoupon = 98;


    @Autowired
    private WxMpService wxMpService;

    @Override
    public List<MallCardSnEntity> queryAll(Map<String, Object> params) {
        return baseMapper.queryAll(params);
    }

    @Override
    public List<MallCardSnEntity> queryDownLoad(Map<String, Object> params) {
        return baseMapper.selectDownLoad(params);
    }

    @Override
    public Page queryPage(Map<String, Object> params) {
        //排序
        params.put("sidx", "T.STATUS");
        params.put("asc", true);
        Page<MallCardSnEntity> page = new Query<MallCardSnEntity>(params).getPage();
        return page.setRecords(baseMapper.selectMallCardSnPage(page, params));
    }

    @Override
    public boolean add(MallCardSnEntity mallCardSn) {
        return this.save(mallCardSn);
    }

    @Override
    public boolean update(MallCardSnEntity mallCardSn) {
        return this.updateById(mallCardSn);
    }

    @Override
    public boolean delete(String cardId) {
        return this.removeById(cardId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteBatch(String[] cardIds) {
        return this.removeByIds(Arrays.asList(cardIds));
    }

    private static AtomicInteger atomicInteger = new AtomicInteger();

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public boolean sendCard(MallOrderEntity orderInfo) {
        boolean flag = true;
        List<MallOrderGoodsEntity> list = orderGoodsService.list(new LambdaQueryWrapper<MallOrderGoodsEntity>()
                .eq(MallOrderGoodsEntity::getOrderId, orderInfo.getId()));
        String goodsId;
        Integer number;
        JSONObject queryParams;
        List<MallCardSnEntity> cardSnEntityList;
        for (MallOrderGoodsEntity item : list) {
            goodsId = item.getGoodsId();
            number = item.getNumber();

            // 扣减卡券SN 1
//            queryParams = new JSONObject();
//            queryParams.put("goodsId", goodsId);
//            queryParams.put("limit", number);
//            queryParams.put("orderGoodsId",item.getId());
//            synchronized (MallCardSnService.class) {
//                // todo 需要验证发券数量为2  库存数量为1的情况
//                if(this.baseMapper.bindingOrderGoods(queryParams) != number){
//                    log.error("**************************发券失败！密钥库存不足！*************************************");
//                    log.error("订单号：{}",orderInfo.getId());
//                    log.error("OrderGoodsId：{}",item.getId());
//                    throw new BusinessException("发券失败！密钥库存不足！");
//                }
//            }
            // 扣减卡券SN 2
            List<String> ids;
            try {
                ids = this.atomGetSNId(goodsId, number);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("**************************发券失败！密钥库存不足！*************************************");
                log.error("订单号：{}", orderInfo.getId());
                log.error("goodsId：{}", orderInfo.getGoodsId());
                log.error("OrderGoodsId：{}", item.getId());
                throw new BusinessException("发券失败！密钥库存不足！");
            }
            this.lambdaUpdate()
                    .set(MallCardSnEntity::getStatus, 1)
                    .set(MallCardSnEntity::getOrderGoodsId, item.getId())
                    .in(MallCardSnEntity::getId, ids)
                    .eq(MallCardSnEntity::getStatus, 0)
                    .update();

            // 发券 1
//            cardSnEntityList = this.lambdaQuery()
//                    .eq(MallCardSnEntity::getGoodsId,goodsId)
//                    .eq(MallCardSnEntity::getOrderGoodsId,item.getId())
//                    .list();
            // 发券 2
            cardSnEntityList = this.lambdaQuery()
                    .in(MallCardSnEntity::getId, ids)
                    .eq(MallCardSnEntity::getOrderGoodsId, item.getId())
                    .list();

            if (cardSnEntityList.size() != number) {
                // 只有缓存数据和 数据库数据不一致的情况下才会到这
                // 比如缓存中未被领取  可是 数据库中实际已经被领取
                throw new BusinessException("发卡失败！请稍后重试。");
            }

            log.info("支付订单：" + orderInfo.getId() + ",用户id：" + orderInfo.getUserId() + "发券：" + cardSnEntityList);
            MallGoodsEntity goodsEntity = goodsService.getById(goodsId);
            try {
                saveUserCard(goodsId, cardSnEntityList, goodsEntity, orderInfo.getUserId(), orderInfo.getPayType(), orderInfo);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("支付订单：" + orderInfo.getId() + "******************************发券失败！");
                throw new BusinessException("发券失败！");
            }
        }
        log.info("支付订单：" + orderInfo.getId() + "===============================发券完成");
        return flag;
    }


    /**
     * @param orderInfo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean sendCard2(MallOrderEntity orderInfo) {
        /*过滤订单金额和商品金额一致的订单*/
        MallOrderGoodsEntity mallOrderGoodsEntity = mallOrderGoodsService.querySumMarketPrice(orderInfo.getId());
        if (mallOrderGoodsEntity.getMarketPrice().compareTo(orderInfo.getActualPrice()) == -1 ||
                orderInfo.getActualPrice().compareTo(mallOrderGoodsEntity.getMarketPrice()) == 0) {
            log.info("订单号：{}，订单金额一致，跳过订单", orderInfo.getOrderSn());
            return true;
        }

        /*修改订单状态*/
        orderInfo.setOrderStatus(Constant.OrderStatus.YFH.getValue());
        orderInfo.setShippingStatus(Constant.ShippingStatus.YFH.getValue());
        orderService.updateById(orderInfo);

        /*订单的所有商品*/
        List<MallOrderGoodsEntity> list = orderGoodsService.list(new LambdaQueryWrapper<MallOrderGoodsEntity>()
                .eq(MallOrderGoodsEntity::getOrderId, orderInfo.getId()));

        String goodsId;
        Integer number;
        List<MallCardSnEntity> cardSnEntityList;

        /*循环订单中所有商品*/
        for (MallOrderGoodsEntity item : list) {
            goodsId = item.getGoodsId();
            number = item.getNumber();
            // 扣减卡券SN 2
            List<String> ids;
            try {
                ids = this.atomGetSNId(goodsId, number);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("**************************发券失败！密钥库存不足！*************************************");
                log.error("订单号：{}", orderInfo.getId());
                log.error("goodsId：{}", orderInfo.getGoodsId());
                log.error("OrderGoodsId：{}", item.getId());
                throw new BusinessException("发券失败！密钥库存不足！");
            }
            this.lambdaUpdate()
                    .set(MallCardSnEntity::getStatus, 1)
                    .set(MallCardSnEntity::getOrderGoodsId, item.getId())
                    .in(MallCardSnEntity::getId, ids)
                    .eq(MallCardSnEntity::getStatus, 0)
                    .update();

            // 发券 2
            cardSnEntityList = this.lambdaQuery()
                    .in(MallCardSnEntity::getId, ids)
                    .eq(MallCardSnEntity::getOrderGoodsId, item.getId())
                    .list();

            if (cardSnEntityList.size() != number) {
                // 比如缓存中未被领取  可是 数据库中实际已经被领取
                throw new BusinessException("发卡失败！请稍后重试。");
            }

            log.info("支付订单：" + orderInfo.getId() + ",用户id：" + orderInfo.getUserId() + "发券：" + cardSnEntityList);
            MallGoodsEntity goodsEntity = goodsService.getById(goodsId);
            try {
                saveUserCard(goodsId, cardSnEntityList, goodsEntity, orderInfo.getUserId(), orderInfo.getPayType(), orderInfo);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
 * 使用 reids 原子操作获取 卡券SN
 * 注意： 调用该方法发券时需要保证，不修改非当前方法返回的SN状态。否则可能导致发重。
            *
            * @param goodsId
 * @param num
 * @return
         */
    public List<String> atomGetSNId(String goodsId, int num) {
        String snKey = Constant.GOODS_SN + goodsId;
        String snNumKey = Constant.GOODS_SN_NUM + goodsId;

        // 校验指定商品 sn 列表是否不存在
        if (jedisUtil.ttl(snNumKey) < 20) {
            // 加锁
            synchronized (snKey.intern()) {
                // 再次判断列表是否不存在
                if (jedisUtil.ttl(snNumKey) < 20) {
                    // 获取 SN 列表
                    JSONObject queryParams = new JSONObject();
                    queryParams.put("goodsId", goodsId);
                    queryParams.put("status", 0);
                    List<String> ids = this.queryCardSnByNumbers(queryParams)
                            .stream()
                            .map(MallCardSnEntity::getId)
                            .collect(Collectors.toList());
                    // 判断是否存在可用SN
                    if (ids.isEmpty()) {
                        throw new BusinessException("密钥库存不足");
                    }
                    // 缓存SN (sn保存时间需要大于数量，需要保证在原子减后可正常获取到ID)
                    jedisUtil.setList(snKey, ids, 1820);
                    // 设置剩余数量
                    jedisUtil.set(snNumKey, String.valueOf(ids.size()), 1800);
                }
            }
        }

        // 使用原子减 获取剩余数量
        long remainNum = jedisUtil.incrBy(snNumKey, -num);
        // 剩余数量是否小于0
        if (remainNum < 0) {
            if (num == 1) {
                // 解决了报错导致剩余卡券领不到的问题，不过会导致每次直接查询到数据库
                jedisUtil.del(snNumKey);
            } else {
                // 扣减数量回退（发券时存在数量大于1的情况下生效。如果发券数量必定为1可以选择删除下方代码，以达成代码优化）
                jedisUtil.incrBy(snNumKey, num);
            }
            // 抛出密钥不足
            throw new BusinessException("密钥库存不足");
        }
        // 通过redis list 截取指定位置的 ID并返回
        return jedisUtil.lrange(snKey, remainNum, remainNum + num - 1);
    }

    /**
     *  1.给一商品发卷，可能是购买一个也可能是多个
     *  2.循环给每个卡券发送
     *  3.同一订单一个商品记录一个user卡券记录
     * @param cardId    商品id
     * @param cardSnEntityList  卡券的数量
     * @param goodsEntity   商品对象
     * @param userId    用户id
     * @param payType   支付类型
     * @param orderEntity   订单对象
     * @return
     * @throws Exception
     */
    private boolean saveUserCard(String cardId, List<MallCardSnEntity> cardSnEntityList, MallGoodsEntity goodsEntity,
                                 String userId, Integer payType, MallOrderEntity orderEntity) throws Exception {
        MallUserCardEntity userCardEntity;
        MallUserEntity userEntity = null;

        for (MallCardSnEntity entity : cardSnEntityList) {//
            userCardEntity = new MallUserCardEntity();
            log.info("卡券类型：" + entity.getType());
            int i=0;

            switch (entity.getType()) {
                // 微信立减金
                case 98:
                    if (userEntity == null) {
                        userEntity = userService.getById(userId);
                    }
                    String[] stockIds = entity.getCardSn().split(",");
                    log.info("卡密：" + entity.getCardSn());

                    StringBuilder couponIds = new StringBuilder();
                    StringBuilder sb =new StringBuilder();

                    for (String stockId : stockIds) {
//                        String requestNo=entity.getId()+(++i);
                        String requestNo=orderEntity.getOrderSn()+String.format("%06d",++i);
                        JSONObject jsonObject = sendCouponWxService.sendCouponV3(userEntity.getOpenId(), stockId,requestNo);
                        String couponId = jsonObject.getString("couponId");

                        couponIds.append(couponId).append(",");
                        sb.append(requestNo).append(",");
                    }

                    String cIds = couponIds.toString();//1
                    if(cIds.lastIndexOf(",") == cIds.length()-1) { //lastIndexOf获取最后一个逗号的索引，判断是否是字符串的最后一位
                        cIds = cIds.substring(0, cIds.length() - 1);
                    }

                    userCardEntity.setCouponId(cIds);
                    userCardEntity.setRequestNos(sb.toString());
                    break;
                // 话费直充
                case 97:
                    // 构建话费充值数据
                    PhoneTopUpDto dto = new PhoneTopUpDto();
                    dto.setOrderId(orderEntity.getId());
                    dto.setCartNo(Long.valueOf(orderEntity.getMobile()));
                    //  todo: 填写充值金额  元为单位
                    dto.setTopUpMoney(goodsEntity.getMarketPrice());
                    dto.setUserId(userId);
                    phoneTopUpService.topUp(dto);
                    break;
                default:
                    break;
            }


            userCardEntity.setCardId(cardId);
            userCardEntity.setType(entity.getType());
            userCardEntity.setCardSn(entity.getCardSn());
            userCardEntity.setExpireTime(DateUtils.addDateDays(new Date(), goodsEntity.getExpireDay()));
            userCardEntity.setUserId(userId);
            userCardEntity.setMarketPrice(goodsEntity.getMarketPrice());
            userCardEntity.setRetailPrice(goodsEntity.getRetailPrice());
            userCardEntity.setAddTime(new Date());
            userCardEntity.setPayType(payType);
            userCardEntity.setOrderId(orderEntity == null ? null : orderEntity.getId());
            userCardService.add(userCardEntity);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_UNCOMMITTED)
    public boolean sendCard(String goodsId, Integer number, MallUserEntity userEntity) {
        MallGoodsEntity goods = goodsService.getById(goodsId);
        if (goods == null) {
            throw new BusinessException("系统繁忙，请稍后再试！");
        }

        // 创建已完成订单  付款金额为 0
        ParamsEntity params = new ParamsEntity();
        params.setFromType(1);
        params.setNumber(1);
        MallOrderEntity order = OrderUtil.getOrder(goods, params, userEntity);
        order.setOrderStatus(Constant.OrderStatus.YFK.getValue());
        order.setShippingStatus(Constant.ShippingStatus.WFH.getValue());
        order.setPayType(4);
        order.setOrderType(7);
        order.setActualPrice(BigDecimal.ZERO);
        order.setOrderPrice(BigDecimal.ZERO);
        order.setConfirmTime(new Date());
        order.setPayTime(new Date());
        orderService.save(order);
        List<MallOrderGoodsEntity> orderGoodsList = OrderUtil.goodsToOrderGoodsList(params, order, goods);
        return orderGoodsService.saveBatch(orderGoodsList);
    }

    @Override
    public List<MallCardSnEntity> queryCardSnByNumbers(JSONObject queryParams) {
        return baseMapper.queryCardSnByNumbers(queryParams);
    }

    @Override
    @Transactional
    public List<MallCardSnEntity> getCardSn(String goodsId, int number) {
        JSONObject queryParams = new JSONObject();
        queryParams.put("goodsId", goodsId);
        queryParams.put("limit", number);
        queryParams.put("status", 0);
        List<MallCardSnEntity> cardSnEntityList = this.queryCardSnByNumbers(queryParams);
        for (MallCardSnEntity entity : cardSnEntityList) {
            entity.setStatus((byte) 1);
            this.updateById(entity);
        }
        return cardSnEntityList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_UNCOMMITTED)
    public void cardSnExport(HttpServletResponse response, Map<String, Object> params) {
        try {
            List<MallCardSnEntity> list = this.queryDownLoad(params);
            //获取查询出来的卡券id
            List<String> ids = list.stream().map(MallCardSnEntity::getId).collect(Collectors.toList());
            //修改状态为已领取
            this.lambdaUpdate().set(MallCardSnEntity::getStatus, Constant.CARD_SN_STATUS_YLQ)
                    .eq(MallCardSnEntity::getStatus, Constant.CARD_SN_STATUS_KY)
                    .in(MallCardSnEntity::getId, ids).update();
            //返回给前端数据
            List<Map<String, Object>> data = new ArrayList<>();
            Map<String, Object> cardMap;
            for (MallCardSnEntity cardSnEntity : list) {
                cardMap = MapUtils.objectToMap(cardSnEntity);
                data.add(cardMap);
            }
            log.info("----导出卡----");
            EasyPoiUtils.cardSnExport(response, data, MallCardSnEntity.class);

        } catch (Exception e) {
            log.error("导出卡异常" + e.getMessage());
            throw new BusinessException("导出错误");
        }

    }
}
